using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL.Fabric
{
    public class Block
    {
        protected string sName;

        protected Dictionary<string, Input> dicInputs = new Dictionary<string, Input>();
        protected Dictionary<string, ControlInput> dicControlInputs = new Dictionary<string, ControlInput>();
        protected Dictionary<string, Output> dicOutputs = new Dictionary<string, Output>();
        protected Dictionary<string, StatusOutput> dicStatusOutputs = new Dictionary<string, StatusOutput>();

        protected Component ParentComponent;
        protected ClockComponent Clock;
        protected NodeVector Reset;
        protected string sBlockType;

        protected string sErrors = "";


        private Dictionary<string, int> dicResizedOutputs = new Dictionary<string,int>();

        public Block(string psName, string psBlockType)
        {
            sName = psName;
            sBlockType = psBlockType;
        }

        public string BlockType
        {
            get { return sBlockType; }
        }

        internal Input CreateInput(string psName,int piWidth)
        {
            Input oInput = new Input(this, psName, piWidth);            
            dicInputs.Add(psName, oInput);
            return oInput;
        }

        internal Output CreateOutput(string psName, int piWidth)
        {
            Output oOutput = new Output(this, psName, piWidth);
            dicOutputs.Add(psName, oOutput);
            return oOutput;
        }

        internal ControlInput CreateControlInput(string psName)
        {
            ControlInput oControl = new ControlInput(this, psName);
            dicControlInputs.Add(psName, oControl);
            return oControl;
        }

        internal StatusOutput CreateStatusOutput(string psName)
        {
            StatusOutput oStatus = new StatusOutput(this, psName);
            dicStatusOutputs.Add(psName, oStatus);
            return oStatus;
        }

        public Dictionary<string,Input> Inputs
        {
            get { return dicInputs; }
        }

        public Dictionary<string, Output> Outputs
        {
            get { return dicOutputs; }
        }

        public Dictionary<string, ControlInput> ControlInputs
        {
            get { return dicControlInputs; }
        }

        public Dictionary<string, StatusOutput> StatusOutputs
        {
            get { return dicStatusOutputs; }
        }

        internal virtual string ValidateStructure()
        {
            string sError = sErrors;
            string sResult = "";
            foreach (Input oInput in dicInputs.Values)
            {
                sResult = oInput.ValidateStructure();
                if (sResult != "")
                    sError += sResult + "\n";
            }

            foreach (Input oInput in dicControlInputs.Values)
            {
                sResult = oInput.ValidateStructure();
                if (sResult != "")
                    sError += sResult + "\n";
            }

            foreach (Output oOutput in dicOutputs.Values)
            {
                sResult = oOutput.ValidateStructure();
                if (sResult != "")
                    sError += sResult + "\n";
            }

            foreach (Output oOutput in dicStatusOutputs.Values)
            {
                sResult = oOutput.ValidateStructure();
                if (sResult != "")
                    sError += sResult + "\n";
            }

            sErrors = sError;

            return sError;
        }

        public string Name
        {
            get { return sName; }
        }

        public override string ToString()
        {
            return sName;
        }

        internal virtual void GenerateStructure()
        {
        }

        internal void BaseGenerateStructure(Component poParentComponent, ClockComponent poClock, NodeVector pnvReset)
        {
            ParentComponent = poParentComponent;
            Clock = poClock;
            Reset = pnvReset;

            GenerateStructure();

            foreach (string sOutput in dicResizedOutputs.Keys)
                GenerateResizedOutput(sOutput, dicResizedOutputs[sOutput]);
        }

        internal void ReportError(string psError)
        {
            sErrors += SharedUtility.BuildError(this.ToString(), psError) + "\n";
        }

        internal void ReportWarning(string psError)
        {
            sErrors += SharedUtility.BuildWarning(this.ToString(), psError) + "\n";
        }

        internal void ExpandJoins()
        {
            foreach (Input oInput in dicInputs.Values)
            {
                oInput.ExpandJoin(ParentComponent);
            }
            foreach (ControlInput oInput in dicControlInputs.Values)
            {
                oInput.ExpandJoin(ParentComponent);
            }
        }

        public virtual void AddSignalViews()
        {
            SystemInterface oSystemInterface = ParentComponent.RapidHardware.SystemInterface;
            foreach (Output oOutput in Outputs.Values)
            {
                oSystemInterface.AddSignalView(oOutput.ToString(), oOutput.NodeVector);
            }
        }

        public Output ResizeOutput(string psOutputName, int piWidth)
        {
            dicResizedOutputs.Add(psOutputName, piWidth);
            string sName = psOutputName + piWidth.ToString();
            return this.CreateOutput(sName, piWidth);            
        }

        private void GenerateResizedOutput(string psOutputName, int piWidth)
        {
            string sName = psOutputName + piWidth.ToString();
            Output oOriginal = Outputs[psOutputName];
            Output oOutput = Outputs[sName];
            oOriginal.NodeVector.Join(oOutput.NodeVector);
            int iOriginalWidth = oOriginal.NodeVector.Width;
            if (iOriginalWidth < piWidth)
                oOutput.NodeVector[iOriginalWidth, piWidth - 1].Join(0);

        }

        public string AddSignalView(string psName)
        {
            NodeVector nvSignal = null;
            String sName = "";

            if (dicInputs.ContainsKey(psName))
            {
                Input oInput = dicInputs[psName];
                nvSignal = oInput.NodeVector;
                sName = oInput.FullName;
            }

            if (dicControlInputs.ContainsKey(psName))
            {
                Input oInput = dicControlInputs[psName];
                nvSignal = oInput.NodeVector;
                sName = oInput.FullName;
            }

            if (dicStatusOutputs.ContainsKey(psName))
            {
                Output oOutput = dicStatusOutputs[psName];
                nvSignal = oOutput.NodeVector;
                sName = oOutput.FullName;
            }

            if (dicOutputs.ContainsKey(psName))
            {
                Output oOutput = dicOutputs[psName];
                nvSignal = oOutput.NodeVector;
                sName = oOutput.FullName;
            }

            if (nvSignal == null)
                throw new Exception("Signal named {" + psName + "} does not exist in block {" + Name + "}");
            ParentComponent.RapidHardware.SystemInterface.AddSignalView(sName, nvSignal);
            return sName;
        }
    }
}
